package lazy.sql;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import lazy2.util.StringUtil;
import org.springframework.jdbc.core.RowMapper;
import ebti.db.annotation.*;

public class DynaRowMapper<T> implements RowMapper<T> {
	private Class<T> clazz;
	private List<SetMethod> setMethods;
	
	private class SetMethod {
		Method method;
		Class<?> paramType;
		String columnName;
	}
	private void setValue(Object invokeTarget, SetMethod setMethod, ResultSet rs) throws Exception {
		Class<?> paramType = setMethod.paramType;
		String columnName = setMethod.columnName;
		Object value = rs.getObject(columnName);
		if(value!=null) {
			if(paramType.equals(Integer.class)) {
				if(value instanceof BigDecimal) {
					value = Integer.valueOf(((BigDecimal)value).intValue());
				} else {
					value = Integer.valueOf(value.toString());
				}
			} else if(paramType.equals(Double.class)) {
				if(value instanceof BigDecimal) {
					value = Double.valueOf(((BigDecimal)value).doubleValue());
				} else {
					value = Double.valueOf(value.toString());
				}
			} else if(paramType.equals(Date.class)) {
				value = rs.getTimestamp(columnName);
			} else if(paramType.equals(String.class)) {
				value = rs.getString(columnName);
			} else {}
		}
		try {
			setMethod.method.invoke(invokeTarget, value);
		} catch (IllegalArgumentException e) {
			String error = new StringBuilder().append("DynaRowMapper：資料庫型態為").append(value.getClass().getName()).append("與物件").append(columnName).append("屬性之").append(paramType.getName()).append("型態不符。").toString();
			System.out.println(error);
			throw e;
		}
	}
	public DynaRowMapper(Class<T> clazz) throws NullPointerException {
		if(clazz==null) throw new NullPointerException("clazz不得為null");
		this.clazz = clazz;
		setMethods = new ArrayList<DynaRowMapper<T>.SetMethod>();
		SetMethod setMethod;
		for(Method method:clazz.getMethods()) {
			if(method.getName().startsWith("set")) {
				if(method.isAnnotationPresent(NotColumn.class) || method.isAnnotationPresent(MultiColumn.class)) continue;
				Class<?>[] types = method.getParameterTypes();
				if(types.length==1) {
					if(types[0].isArray()) continue;
					setMethod = new SetMethod();
					setMethod.method = method;
					setMethod.paramType = types[0];
					Column col = method.getAnnotation(Column.class);
					if(col!=null) {
						setMethod.columnName = col.value();
					} else {
						setMethod.columnName = StringUtil.toProperty(method.getName());
					}
					setMethods.add(setMethod);
				}
			}
		}
	}
	@Override
	public T mapRow(ResultSet rs, int i) throws SQLException {
		T o = null;
		try {
			o = clazz.newInstance();
			for(SetMethod setMethod:setMethods) {
				setValue(o, setMethod, rs);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}	
		return o;
	}
}
